﻿using Ast.Framework.Caching;
using Ast.Framework.Environment.Extensions;
using Ast.Framework.Environment.Extensions.Compilers;
using Ast.Framework.Environment.Extensions.Folders;
using Ast.Framework.Environment.Extensions.Loaders;
using Ast.Framework.Events;
using Ast.Framework.Exceptions;
using Ast.Framework.FileSystems.Dependencies;
using Ast.Framework.FileSystems.Paths;
using Ast.Framework.FileSystems.SiteFolders;
using Ast.Framework.Forms;
using Ast.Framework.Logging;
using Ast.Framework.Services;
using Ast.Framework.Settings;
using Autofac;

namespace Ast.Framework.Environment
{
    public static class HostStarter
    {
        public static IContainer CreateHostContainer()
        {
            var builder = new ContainerBuilder();

            // modules
            builder.RegisterModule(new LoggingFactoryModule());
            builder.RegisterModule(new LoggingModule());
            builder.RegisterModule(new EventsModule());
            builder.RegisterModule(new CacheModule());
            builder.RegisterModule(new SettingModule());
            builder.RegisterModule(new FormModule());

            // event bus
            builder.RegisterType<DefaultEventBus>().As<IEventBus>().SingleInstance();
            builder.RegisterType<DefaultExceptionPolicy>().As<IExceptionPolicy>().SingleInstance();

            // cache
            builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
            builder.RegisterType<DefaultCacheContextAccessor>().As<ICacheContextAccessor>().SingleInstance();
            builder.RegisterType<DefaultParallelCacheContext>().As<IParallelCacheContext>().SingleInstance();
            builder.RegisterType<DefaultAsyncTokenProvider>().As<IAsyncTokenProvider>().SingleInstance();

            // volatile providers
            RegisterVolatileProvider<Clock, IClock>(builder);
            RegisterVolatileProvider<DefaultPathMonitor, IPathMonitor>(builder);
            RegisterVolatileProvider<DefaultPathProvider, IPathProvider>(builder);
            RegisterVolatileProvider<SiteFolder, ISiteFolder>(builder);
            RegisterVolatileProvider<DefaultAssemblyProbingFolder, IAssemblyProbingFolder>(builder);
            RegisterVolatileProvider<DefaultDependenciesFolder, IDependenciesFolder>(builder);

            // folders
            builder.RegisterType<SiteFolderRoot>().As<ISiteFolderRoot>().SingleInstance();
            builder.RegisterType<ModuleFolderRoot>().As<IModuleFolderRoot>().SingleInstance();

            // assembly resolvers
            builder.RegisterType<DefaultAssemblyLoader>().As<IAssemblyLoader>().SingleInstance();
            builder.RegisterType<AppDomainAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
            builder.RegisterType<GacAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
            builder.RegisterType<FrameworkAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();

            // extensions
            builder.RegisterType<ExtensionLoaderCoordinator>().As<IExtensionLoaderCoordinator>().SingleInstance();
            builder.RegisterType<ExtensionMonitoringCoordinator>().As<IExtensionMonitoringCoordinator>().SingleInstance();
            builder.RegisterType<ExtensionManager>().As<IExtensionManager>().SingleInstance();
            builder.RegisterType<DefaultBuildManager>().As<IBuildManager>().SingleInstance();

            // extension compilers
            builder.RegisterType<DefaultExtensionCompiler>().As<IExtensionCompiler>().SingleInstance();
            builder.RegisterType<DefaultCriticalErrorProvider>().As<ICriticalErrorProvider>().SingleInstance();
            builder.RegisterType<DefaultProjectFileParser>().As<IProjectFileParser>().SingleInstance();
            builder.RegisterType<ExtensionHarvester>().As<IExtensionHarvester>().SingleInstance();
            builder.RegisterType<ModuleFolders>().As<IExtensionFolders>().SingleInstance()
                .WithParameter((info, context) => info.Name == "paths", (info, context) => new[] {context.Resolve<IModuleFolderRoot>().Path});

            // extension loaders
            builder.RegisterType<DynamicExtensionLoader>().As<IExtensionLoader>().SingleInstance();
            builder.RegisterType<ReferencedExtensionLoader>().As<IExtensionLoader>().SingleInstance();
            builder.RegisterType<PrecompiledExtensionLoader>().As<IExtensionLoader>().SingleInstance();

            //
            builder.RegisterType<DefaultHost>().As<IHost>().SingleInstance();

            var container = builder.Build();

            return container;
        }

        private static void RegisterVolatileProvider<TRegister, TService>(ContainerBuilder builder) where TService : IVolatileProvider
        {
            builder.RegisterType<TRegister>()
                .As<TService>()
                .As<IVolatileProvider>()
                .SingleInstance();
        }

        public static IHost CreateHost()
        {
            var container = CreateHostContainer();
            return container.Resolve<IHost>();
        }
    }
}